/* MQTT over SSL Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_system.h"
#include "esp_partition.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"


#include "esp_log.h"
#include "mqtt_client.h"
#include "esp_tls.h"
#include "esp_ota_ops.h"
#include <sys/param.h>
#include "mqtt_ssl.h"
#include "protocol_examples_common.h"

#include "cJSON.h"
#include "../RELAY/bsp_relay.h"

static const char *TAG = "MQTT_EXAMPLE";

// 与RC522卡相关的定义
char read_data_str[64] = {0};
extern char card_id[12];
extern uint8_t *read_data;

// 增加密码相关定义
#define MAX_PASSWORD_LENGTH 8
char system_password[MAX_PASSWORD_LENGTH + 1] = "123456"; // 默认密码
char input_password[MAX_PASSWORD_LENGTH + 1] = {0}; // 当前输入的密码
int password_index = 0; // 当前密码输入位置

#define MAX_CARD_COUNT 10
char card_list[MAX_CARD_COUNT][16]; // 最多保存10张卡，每张卡ID最长16字符
esp_mqtt_client_handle_t client; // MQTT客户端句柄，指向当前的mqtt连接
esp_mqtt_event_handle_t event; // 移除const限定符

int card_count = 0; // 已存储的卡数量

// 初始化第一张卡
void init_card_list(void) {
    strcpy(card_list[0], "6947BDA3"); // 添加默认卡
    card_count = 1;
}

// 检查卡ID是否在授权列表中
bool is_card_authorized(const char* card_id) {
    printf("验证卡号: %s，当前授权卡片数量: %d\n", card_id, card_count);
    for (int i = 0; i < card_count; i++) {
        printf("对比卡片 [%d]: %s\n", i, card_list[i]);
        if (strcmp(card_id, card_list[i]) == 0) {
            printf("找到匹配卡片，验证通过\n");
            return true;
        }
    }
    printf("未找到匹配卡片，验证失败\n");
    return false;
}

// 添加新卡到列表
bool add_card_to_list(const char* new_card) {
    // 检查卡是否已存在
    for (int i = 0; i < card_count; i++) {
        if (strcmp(new_card, card_list[i]) == 0) {
            return false; // 卡已存在
        }
    }
    
    // 检查是否已达到最大卡数
    if (card_count >= MAX_CARD_COUNT) {
        return false; // 卡列表已满
    }
    
    // 添加新卡
    strcpy(card_list[card_count], new_card);
    card_count++;
    return true;
}

// 增加密码验证函数
bool verify_password(const char* pwd) {
    if (pwd == NULL) {
        return false;
    }
    return (strcmp(pwd, system_password) == 0);
}

// 设置新密码
bool set_password(const char* new_pwd) {
    if (new_pwd == NULL || strlen(new_pwd) == 0 || strlen(new_pwd) > MAX_PASSWORD_LENGTH) {
        return false;
    }
    
    // 检查是否只包含数字 (矩阵键盘输入限制)
    for (int i = 0; i < strlen(new_pwd); i++) {
        if (new_pwd[i] < '0' || new_pwd[i] > '9') {
            return false;
        }
    }
    
    strcpy(system_password, new_pwd);
    printf("密码已更新为: %s\n", system_password);
    return true;
}

// 重置密码输入
void reset_password_input(void) {
    // 重置输入的密码
    memset(input_password, 0, sizeof(input_password));
    password_index = 0;
    printf("密码输入已重置\n");
}

// 添加密码字符
bool add_password_char(char ch) {
    if (password_index < MAX_PASSWORD_LENGTH) {
        input_password[password_index++] = ch;
        input_password[password_index] = '\0'; // 始终保持字符串结束符
        printf("添加密码字符: %c, 当前密码长度: %d\n", ch, password_index);
        return true;
    }
    printf("密码长度已达上限: %d\n", MAX_PASSWORD_LENGTH);
    return false;
}

// 检查当前输入的密码
bool check_current_password() {
    bool result = verify_password(input_password);
    printf("验证密码: %s, 结果: %s\n", input_password, result ? "通过" : "失败");
    return result;
}

#if CONFIG_BROKER_CERTIFICATE_OVERRIDDEN == 1
static const uint8_t mqtt_eclipseprojects_io_pem_start[]  = "-----BEGIN CERTIFICATE-----\n" CONFIG_BROKER_CERTIFICATE_OVERRIDE "\n-----END CERTIFICATE-----";
#else
extern const uint8_t _binary_emqxsl_ca_crt_start[] asm("_binary_emqxsl_ca_crt_start");
#endif
extern const uint8_t _binary_emqxsl_ca_crt_end[] asm("_binary_emqxsl_ca_crt_end");

//
// Note: this function is for testing purposes only publishing part of the active partition
//       (to be checked against the original binary)
//
static void send_binary(esp_mqtt_client_handle_t client)
{
    esp_partition_mmap_handle_t out_handle;
    const void *binary_address;
    const esp_partition_t *partition = esp_ota_get_running_partition();
    esp_partition_mmap(partition, 0, partition->size, ESP_PARTITION_MMAP_DATA, &binary_address, &out_handle);
    // sending only the configured portion of the partition (if it's less than the partition size)
    int binary_size = MIN(CONFIG_BROKER_BIN_SIZE_TO_SEND, partition->size);
    int msg_id = esp_mqtt_client_publish(client, "/topic/binary", binary_address, binary_size, 0, 0);
    ESP_LOGI(TAG, "binary sent with msg_id=%d", msg_id);
}

/*
 * @brief Event handler registered to receive MQTT events
 *
 *  This function is called by the MQTT client event loop.
 *
 * @param handler_args user data registered to the event.
 * @param base Event base for the handler(always MQTT Base in this example).
 * @param event_id The id for the received event.
 * @param event_data The data for the event, esp_mqtt_event_handle_t.
 */
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%" PRIi32, base, event_id);
    event = event_data;
    client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id) {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
         msg_id = esp_mqtt_client_subscribe(client, "/readcard", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

         msg_id = esp_mqtt_client_subscribe(client, "/writecard", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
         
         msg_id = esp_mqtt_client_subscribe(client, "/getcards", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

         // 添加对实验室门禁控制主题的订阅
         msg_id = esp_mqtt_client_subscribe(client, "/lab_access", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
         
         // 添加对密码设置主题的订阅
         msg_id = esp_mqtt_client_subscribe(client, "/set_password", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
         
         msg_id = esp_mqtt_client_subscribe(client, "/password", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
         
         // 添加获取当前密码主题的订阅
         msg_id = esp_mqtt_client_subscribe(client, "/get_password", 0);
         ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        // msg_id = esp_mqtt_client_unsubscribe(client, "/topic/qos1");
        // ESP_LOGI(TAG, "sent unsubscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        // msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
        // ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        if (strncmp(event->data, "send binary please", event->data_len) == 0) {
            ESP_LOGI(TAG, "Sending the binary");
            send_binary(client);
        }
        
if (*(char *)event->data == '1')

        {
          cJSON *json = cJSON_CreateObject();
        // 添加键值对到JSON对象
        cJSON_AddStringToObject(json, "card_id", card_id);
//        const char *read_data_str = (const char *)read_data;
        cJSON_AddStringToObject(json, "information", read_data_str);

        // 将JSON对象转换为字符串
        char *json_string = cJSON_Print(json);
        // 
        printf("publish_message: %s\r\n", json_string);
        esp_mqtt_client_publish(client, "/cardID", json_string, strlen(json_string), 2, 0);
        }
            // 收到消息
            // if (strncmp(event->topic, "/writecard", event->topic_len) == 0) {
            //     // 拷贝新的 ID 号
            //     strncpy(new_card_id, event->data, sizeof(new_card_id));
            //     new_card_id[sizeof(new_card_id) - 1] = '\0'; // 确保字符串结束符
            //     printf("Received new card ID: %s\n", new_card_id);
            // }
// 收到消息
if (strncmp(event->topic, "/writecard", event->topic_len) == 0) {
    // 解析 JSON 数据
    cJSON *root = cJSON_Parse(event->data);
    if (root != NULL) {
        // 获取卡号
        cJSON *card_number = cJSON_GetObjectItem(root, "card_number");
        if (card_number != NULL && cJSON_IsString(card_number)) {
            printf("收到卡片录入请求 - 卡号: %s\n", card_number->valuestring);
            // 检查卡是否已存在
            if (is_card_authorized(card_number->valuestring)) {
                printf("卡片已存在: %s\n", card_number->valuestring);
            } else {
                // 添加新卡到列表
                if (add_card_to_list(card_number->valuestring)) {
                    printf("成功添加新卡片: %s，当前卡片数量: %d\n", card_number->valuestring, card_count);
                    // 打印当前所有卡片ID，方便调试
                    printf("当前所有已授权卡片:\n");
                    for (int i = 0; i < card_count; i++) {
                        printf("  [%d] %s\n", i, card_list[i]);
                    }
                } else {
                    printf("添加新卡片失败: %s\n", card_number->valuestring);
                }
            }
        } else {
            printf("JSON中未找到card_number字段或格式不正确\n");
        }
        cJSON_Delete(root);
    } else {
        printf("解析JSON数据失败: %.*s\n", event->data_len, event->data);
    }
}
// 如果收到获取卡列表的请求
else if (strncmp(event->topic, "/getcards", event->topic_len) == 0) {
    // 创建JSON对象来保存卡列表
    cJSON *json = cJSON_CreateObject();
    cJSON *cards = cJSON_CreateArray();
    
    // 添加所有卡到JSON数组
    for (int i = 0; i < card_count; i++) {
        cJSON_AddItemToArray(cards, cJSON_CreateString(card_list[i]));
    }
    
    // 添加卡数组到JSON对象
    cJSON_AddItemToObject(json, "cards", cards);
    cJSON_AddNumberToObject(json, "count", card_count);
    
    // 将JSON对象转换为字符串
    char *json_string = cJSON_PrintUnformatted(json);
    
    // 发布卡列表
    esp_mqtt_client_publish(client, "/cardlist", json_string, strlen(json_string), 0, 0);
    
    // 释放JSON字符串内存
    free(json_string);
    cJSON_Delete(json);
}
// 处理实验室门禁控制命令
else if (strncmp(event->topic, "/lab_access", event->topic_len) == 0) {
    // 解析JSON数据
    cJSON *root = cJSON_Parse(event->data);
    if (root != NULL) {
        // 获取实验室编号和操作
        cJSON *lab_number = cJSON_GetObjectItem(root, "lab_number");
        cJSON *operation = cJSON_GetObjectItem(root, "operation");
        
        if (lab_number != NULL && cJSON_IsString(lab_number) && 
            operation != NULL && cJSON_IsString(operation)) {
            
            printf("收到门禁控制命令: 实验室=%s, 操作=%s\n", 
                   lab_number->valuestring, operation->valuestring);
            
            // 只响应T101实验室的开门命令
            if (strcmp(lab_number->valuestring, "T101") == 0 && 
                strcmp(operation->valuestring, "OPEN") == 0) {
                extern void Set_Relay_Switch(uint8_t state);
                // 控制继电器开启
                Set_Relay_Switch(1);
                printf("T101实验室门已开启\n");
            } else {
                printf("非本实验室的命令，忽略\n");
            }
        }
        cJSON_Delete(root);
    } else {
        printf("解析JSON数据失败\n");
    }
}
// 处理密码设置
else if (strncmp(event->topic, "/set_password", event->topic_len) == 0) {
    // 解析JSON数据
    cJSON *root = cJSON_Parse(event->data);
    if (root != NULL) {
        // 获取新密码
        cJSON *password = cJSON_GetObjectItem(root, "password");
        if (password != NULL && cJSON_IsString(password)) {
            printf("收到密码设置请求 - 新密码: %s\n", password->valuestring);
            
            // 设置新密码
            if (set_password(password->valuestring)) {
                printf("成功设置新密码: %s\n", system_password);
                
                // 发送成功响应
                cJSON *response = cJSON_CreateObject();
                cJSON_AddStringToObject(response, "status", "success");
                cJSON_AddStringToObject(response, "message", "密码设置成功");
                char *response_str = cJSON_PrintUnformatted(response);
                esp_mqtt_client_publish(client, "/password_response", response_str, strlen(response_str), 0, 0);
                free(response_str);
                cJSON_Delete(response);
            } else {
                printf("设置新密码失败\n");
                
                // 发送失败响应
                cJSON *response = cJSON_CreateObject();
                cJSON_AddStringToObject(response, "status", "error");
                cJSON_AddStringToObject(response, "message", "密码格式不正确");
                char *response_str = cJSON_PrintUnformatted(response);
                esp_mqtt_client_publish(client, "/password_response", response_str, strlen(response_str), 0, 0);
                free(response_str);
                cJSON_Delete(response);
            }
        } else {
            printf("JSON中未找到password字段或格式不正确\n");
        }
        cJSON_Delete(root);
    } else {
        printf("解析JSON数据失败: %.*s\n", event->data_len, event->data);
    }
}
// 处理/password主题 (兼容旧格式)
else if (strncmp(event->topic, "/password", event->topic_len) == 0) {
    // 增加原始数据打印
    printf("收到/password主题数据: %.*s\n", event->data_len, event->data);
    
    // 解析JSON数据
    cJSON *root = cJSON_Parse(event->data);
    if (root != NULL) {
        // 获取action字段
        cJSON *action = cJSON_GetObjectItem(root, "action");
        if (action != NULL && cJSON_IsString(action) && strcmp(action->valuestring, "set_password") == 0) {
            // 获取密码字段
            cJSON *password = cJSON_GetObjectItem(root, "password");
            if (password != NULL && cJSON_IsString(password)) {
                printf("收到密码设置请求(旧格式) - 新密码: %s\n", password->valuestring);
                
                // 设置新密码
                if (set_password(password->valuestring)) {
                    printf("成功设置新密码: %s\n", system_password);
                    
                    // 测试新密码验证 - 模拟输入密码触发继电器
                    reset_password_input();  // 重置当前输入
                    
                    // 逐个字符输入密码
                    const char* pwd = password->valuestring;
                    for (int i = 0; i < strlen(pwd); i++) {
                        add_password_char(pwd[i]);
                    }
                    
                    // 验证密码并触发继电器
                    if (check_current_password()) {
                        printf("Web密码验证成功，触发继电器\n");
                        extern void Set_Relay_Switch(uint8_t state);
                        Set_Relay_Switch(1);  // 打开继电器
                        
                        // 3秒后关闭继电器
                        vTaskDelay(3000 / portTICK_PERIOD_MS);
                        Set_Relay_Switch(0);
                    }
                    
                    // 发送成功响应
                    cJSON *response = cJSON_CreateObject();
                    cJSON_AddStringToObject(response, "status", "success");
                    cJSON_AddStringToObject(response, "message", "密码设置成功");
                    char *response_str = cJSON_PrintUnformatted(response);
                    esp_mqtt_client_publish(client, "/password_response", response_str, strlen(response_str), 0, 0);
                    free(response_str);
                    cJSON_Delete(response);
                } else {
                    printf("设置新密码失败\n");
                    
                    // 发送失败响应
                    cJSON *response = cJSON_CreateObject();
                    cJSON_AddStringToObject(response, "status", "error");
                    cJSON_AddStringToObject(response, "message", "密码格式不正确");
                    char *response_str = cJSON_PrintUnformatted(response);
                    esp_mqtt_client_publish(client, "/password_response", response_str, strlen(response_str), 0, 0);
                    free(response_str);
                    cJSON_Delete(response);
                }
            }
        }
        cJSON_Delete(root);
    }
}
// 获取当前密码
else if (strncmp(event->topic, "/get_password", event->topic_len) == 0) {
    // 创建JSON对象来保存密码
    cJSON *json = cJSON_CreateObject();
    cJSON_AddStringToObject(json, "password", system_password);
    
    // 将JSON对象转换为字符串
    char *json_string = cJSON_PrintUnformatted(json);
    
    // 发布密码信息
    esp_mqtt_client_publish(client, "/password_info", json_string, strlen(json_string), 0, 0);
    
    // 释放JSON字符串内存
    free(json_string);
    cJSON_Delete(json);
}
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
            ESP_LOGI(TAG, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err);
            ESP_LOGI(TAG, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err);
            ESP_LOGI(TAG, "Last captured errno : %d (%s)",  event->error_handle->esp_transport_sock_errno,
                     strerror(event->error_handle->esp_transport_sock_errno));
        } else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) {
            ESP_LOGI(TAG, "Connection refused error: 0x%x", event->error_handle->connect_return_code);
        } else {
            ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type);
        }
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

 void mqtt_app_start(void)
{
    init_card_list();
    const esp_mqtt_client_config_t mqtt_cfg = {
        .broker = {
            .address.uri = CONFIG_BROKER_URI,
            .verification.certificate = (const char *)_binary_emqxsl_ca_crt_start
    },
    .credentials = {
            .username = "GYJ_LOCK",
            .authentication = {
            .password = "2140707160",
            },
        }
    };

    ESP_LOGI(TAG, "[APP] Free memory: %" PRIu32 " bytes", esp_get_free_heap_size());
    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
    /* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);

    // int msg_id;
    // msg_id = esp_mqtt_client_enqueue(client, "/readcard", "data_3", 0, 1, 0, true);
    // ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id);
    // msg_id = esp_mqtt_client_enqueue(client, "/cardID", "QoS2 message", 0, 2, 0, true);
    // ESP_LOGI(TAG, "Enqueued msg_id=%d", msg_id);

    esp_mqtt_client_start(client);
}
